\chapter{Testy}
Aby osiągnąć wysokiej jakości kod, należy przeprowadzać jego testy. Jest to szczególnie ważne przy tworzeniu bibliotek, gdyż wszelkie błędy w~nich zawarte wpływają negatywnie na programy klientów.
Biblioteką odpowiedzialną za testy w~Qt jest QtTestLib~\cite{Qt:TestLib}. Biblioteka ta udostępnia narzędzia umożliwiające tworzenie, m.in. testów jednostkowych, testów sterowanych danymi oraz testów wydajnościowych. Jako, że do testowania mojej biblioteki wykorzystam właśnie tę platformę, poniżej opisuję pokrótce jej możliwości. Następnie opisuję sposób i~przypadki testowe, które zostaną sprawdzone na mojej bibliotece.

\section{QtTestLib}
Stworzenie aplikacji testowej w~QtTestLib składa się z~kilku kroków. Pierwszym z~nich jest stworzenie klasy będącej swego rodzaju zbiorem testów jednostkowych. Następnie trzeba zaimplementować same testy jednostkowe, oraz zasilić je danymi, które umożliwią weryfikację poprawności działania testowanego oprogramowania. Kolejnym, opcjonalnym, krokiem jest napisanie testów wydajnościowych. Ostatnim etapem jest stworzenie funkcji \textit{main()}, zbudowanie oraz uruchomienie testów.

\subsection{Klasa testowa}
Podstawą testów jednostkowych jest klasa, którą należy stworzyć. Musi ona dziedziczyć po QObject, a~wszystkie testy jednostkowe muszą być realizowane w~metodach tej klasy. Z~kolei metody te muszą być jej prywatnymi slotami. Zabieg ten jest konieczny, aby QtTestLib mógł wykryć wszystkie nasze testy jednostkowe.


\subsection{Testy jednostkowe}
Testy jednostkowe są zazwyczaj prostymi kawałkami kodu, w~których sprawdzany jest efekt wywołania metody testowanej klasy. Jeśli jest on zgodny z~oczekiwaniami to test jest zaliczany i~system przechodzi do następnego testu. W~przeciwnym przypadku test jest oblewany, a~informacja o~tym zdarzeniu zostaje zapisana w~logu. W~zależności od ustawień aplikacj testowej, może ona zostać w~tym momencie przerwana, bądź kontynuowana. 
QtTestLib udostępnia swoją funkcjonalność za pomocą zbioru makrodefinicji, np:
\begin{itemize}
\item{QVERIFY(warunek) -- sprawdzenie bool-owskiej wartości. Prawda zalicza test.}
\item{QCOMPARE(faktyczna,~oczekiwana) -- porównanie dwóch wartości. Równość zalicza test.}
\end{itemize}


\subsection{Testy sterowane danymi}
Aby stworzyć test sterowany danymi, należy dodać do klasy testowej dwa sloty~\ref{code:slots}.
\begin{lstlisting}[caption=Slot z danymi, label=code:slots]
void someTest();
void someTest_data();
\end{lstlisting}
Pierwszy ze slotów odpowiada za test jednostkowy, natomiast drugi za dostarczenie danych do owego testu. Takie odseparowanie logiki od danych ułatwia dodawanie nowych danych, gdyż nie powoduje zmian w~kodzie logiki testu. Technika ta bardziej szczegółowo została omówiona w~artykule~\footnote{Testy sterowane danymi \url{http://qt-project.org/doc/qt-4.8/qtestlib-tutorial2.html}}.

\subsection{Testy wydajnościowe}
Tworzenie testów wydajnościowych, czyli tzw. benchmark-ów, jest możliwe za pomocą makra \textit{QBENCHMARK}. Przykładowy test wydajności znajduje się w~wydruku~\ref{code:test}.
\begin{lstlisting}[caption=Test wydajności, label=code:test]
void TestFoo::simpleTest()
{
	Foo foo;

	QVERIFY(foo.doSomething());

	QBENCHMARK 
	{
		foo.doSomething();
	}
}
\end{lstlisting}

Jak widać, w~jednym miejscu łączone są tutaj dwa testy metody \newline \textit{Foo::doSomething()}. Pierwszy z~nich to poprostu weryfikacja poprawności jej działania. Drugi test odpowiada za test wydajności tej metody. Za pomocą tej techniki oraz testów sterowanych danymi można stworzyć automatyczne testy porównujące wydajność danego rozwiązania dla różnych zbiorów danych.

\subsection{Funkcja main}
Utworzenie funkcji main naszego testu sprowadza się do wykorzystania makra, które jako argument przyjmuje nazwę naszej klasy testowej, np.
\textit{QTEST\_MAIN(TestFoo)}. Tak utworzona funkcja main spowoduje uruchomienie każdego testu jednostkowego, dla każdego przygotowanego dlań zbioru danych.

\subsection{Uruchomienie testu}
Aby zbudować naszą testową aplikację należy wpisać w~konsoli poniższe komendy~\ref{code:test:build}.
\begin{lstlisting}[caption=Budowanie aplikacji testowej, label=code:test:build]
qmake -project "QT += testlib"
qmake
make
\end{lstlisting}

Następnie należy uruchomić stworzony plik wykonywalny. Jako rezultat jego działania otrzymamy plik z~logami, które należy przeanalizować.

\section{Przypadki testowe}
Poniżej opisuję przypadki testowe przewidziane dla mojej biblioteki. Aplikacje testujące te przypadki są dostępne w~repozytorium projektu, w~katalogu trunk/test. Każdy z~tych testów powinien być uruchamiany podczas testów regresji~\cite{Sacha}.

\subsection{Mapowanie współrzędnych}
Przetestowałem mapowanie globalnych współrzędnych widoku na lokalne współrzędne wykresu oraz odwrotnie. Poprawność działania tego mechanizmu jest niezbędna do realizacji interaktywności. Testy mapowań karmiłem zarówno poprawnymi, jak i~błędnymi danymi. W~obu przypadkach testy potwierdziły właściwe zachowanie mojej biblioteki. Na wydruku~\ref{test:coords} przedstawiam pełen log aplikacji testowej.


\begin{lstlisting}[caption=Test mapowania współrzędnych, label=test:coords]
********* Start testing of TestSeries *********
Config: Using QtTest library 5.1.0, Qt 5.1.0
PASS   : TestSeries::initTestCase()
PASS   : TestSeries::mapFromGlobal(top left)
PASS   : TestSeries::mapFromGlobal(bottom right)
PASS   : TestSeries::mapFromGlobal(center)
PASS   : TestSeries::mapFromGlobal(top border)
PASS   : TestSeries::mapFromGlobal(left border)
XFAIL  : TestSeries::mapFromGlobal(intentional fail) 
	This test should have failed.
	Loc: [../seriesTest/testseries.cpp(21)]
PASS   : TestSeries::mapFromGlobal(intentional fail)
PASS   : TestSeries::mapToGlobal(top left)
PASS   : TestSeries::mapToGlobal(bottom right)
PASS   : TestSeries::mapToGlobal(center)
PASS   : TestSeries::mapToGlobal(top border)
PASS   : TestSeries::mapToGlobal(left border)
XFAIL  : TestSeries::mapToGlobal(intentional fail) 
	This test should have failed.
	Loc: [../seriesTest/testseries.cpp(50)]
PASS   : TestSeries::mapToGlobal(intentional fail)
PASS   : TestSeries::cleanupTestCase()
Totals: 14 passed, 0 failed, 0 skipped
********* Finished testing of TestSeries *********
\end{lstlisting}

\subsection{Adapter modelu}
Sporządziłem test adaptera modelu, w~którym wstawiam, modyfikuję oraz usuwam dane z~adaptera. Przetestowałem działanie adaptera jedynie dla modelu klasy \textit{QocDataSeries}. Po dodaniu do biblioteki obsługi nowych modeli należy uzupełnić metodę z~wydruku~\ref{test:data}.

\begin{lstlisting}[caption=Dane sterujące testami, label=test:data]
void TestAdaptorModel::insertModels()
{
	QTest::addColumn<QVariant>("model");

	QTest::newRow("QocDataSeries") << 
	QVariant::fromValue(new QocDataSeries(this));
}
\end{lstlisting}

Zaliczenie tego testu jest szczególnie ważne, gdyż ewentualne błędy we współpracy adaptera z~faktycznym źródłem danych będzie negatywnie wpływać na stabilność całej biblioteki. Poniżej znajduje się log~\ref{test:adaptor} testu.

\begin{lstlisting}[caption=Test adptera modelu, label=test:adaptor]
********* Start testing of TestAdaptorModel *********
Config: Using QtTest library 5.1.0, Qt 5.1.0
PASS   : TestAdaptorModel::initTestCase()
PASS   : TestAdaptorModel::insert(QocDataSeries)
PASS   : TestAdaptorModel::modify(QocDataSeries)
PASS   : TestAdaptorModel::remove(QocDataSeries)
PASS   : TestAdaptorModel::cleanupTestCase()
Totals: 5 passed, 0 failed, 0 skipped
********* Finished testing of TestAdaptorModel *********
\end{lstlisting}

\subsection{Wydajność animacji}
Postanowiłem przetestować skalowalność odrysowywania wykresu słupkowego podczas animacji. Parametrami zmiennymi tesu były rozmiar okna oraz wielkość zbioru próbek. Podobny test trzeba będzie przeprowadzić dla każdego nowego wykresu dodawanego do biblioteki.

Ten test wyjatkowo nie został wykonany za pomocą QtTestLib, a~polega na porównywaniu liczby uzyskanych w~animacji klatek. Odbywa się to poprzez zliczanie zdarzeń typu \textit{QEvent::Paint}, odpowiedzialnych za odrysowywanie widgetu. Po zakończeniu się animacji liczba odrysowań zostaje wyrzucona na konsolę. Każda z~animacji trwała dokładnie 2 sekundy, a~w czasie ich trwania słupki stopniowo ,,rosły'', ostatecznie osiągając swoją oczekiwaną wysokość. Testy odbywały się na komputerze wyposażonym w~czterordzeniowy procesor AMD Phenom~II 3,2~GHz, 4GB pamięci RAM oraz kartę graficzną ATI Radeon HD~6850.

Tablica~\ref{bench:seq} zawiera wyniki sekwencyjnej animacji wysokości słupków jednego wykresu. Animacja kolejnego słupka zaczynała się dopiero, gdy animacja poprzedniego słupka została zakończona. W~tablicy~\ref{bench:par} umieściłem wyniki analogicznego testu dla animacji równoległej. Wykonałem również test podczas którego animowałem jednocześnie dwa wykresy. Jego wyniki przedstawia tablica~\ref{bench:two}.

\begin{table}\footnotesize
\centering
\caption{Wyniki animacji sekwencyjnej}\label{bench:seq}
\label{tab:wykresy}
\begin{tabular}{|c|c|c|c|c|c|}
\hline
Rozmiar okna & Liczba próbek danych & FPS\\
\hline
600x480 & 10 & 62\\
\hline
600x480 & 100 & 62\\
\hline
600x480 & 1000 & 34\\
\hline
800x600 & 10 & 62\\
\hline
800x600 & 100 & 62\\
\hline
800x600 & 1000 & 26\\
\hline
1024x768 & 10 & 62\\
\hline
1024x768 & 100 & 61\\
\hline
1024x768 & 1000 & 20\\
\hline
1920x1080 & 10 & 62\\
\hline
1920x1080 & 100 & 47\\
\hline
1920x1080 & 1000 & 13\\
\hline
\end{tabular}
\end{table}

\begin{table}\footnotesize
\centering
\caption{Wyniki animacji równoległej}\label{bench:par}
\label{tab:wykresy}
\begin{tabular}{|c|c|c|c|c|c|}
\hline
Rozmiar okna & Liczba próbek danych & FPS\\
\hline
600x480 & 10 & 62\\
\hline
600x480 & 100 & 62\\
\hline
600x480 & 1000 & 32\\
\hline
800x600 & 10 & 62\\
\hline
800x600 & 100 & 62\\
\hline
800x600 & 1000 & 27\\
\hline
1024x768 & 10 & 62\\
\hline
1024x768 & 100 & 62\\
\hline
1024x768 & 1000 & 22\\
\hline
1920x1080 & 10 & 62\\
\hline
1920x1080 & 100 & 49\\
\hline
1920x1080 & 1000 & 16\\
\hline
\end{tabular}
\end{table}

\begin{table}\footnotesize
\centering
\caption{Wyniki animacji dwóch wykresów}\label{bench:two}
\label{tab:wykresy}
\begin{tabular}{|c|c|c|c|c|c|}
\hline
Rozmiar okna & Typ animacji & Liczba próbek danych & FPS\\
\hline
1024x768 & Sekwencyjna & 10 & 62\\
\hline
1024x768 & Sekwencyjna &  100 & 52\\
\hline
1024x768 & Sekwencyjna &  1000 & 12\\
\hline
1024x768 & Równoległa & 10 & 62\\
\hline
1024x768 & Równoległa &  100 & 55\\
\hline
1024x768 & Równoległa &  1000 & 12\\
\hline
\end{tabular}
\end{table}


%TODO: Próbowałem wykorzystać tutaj QBENCHMARK, ale średnio się sprawdziło.




